package io.gitee.caoxiaoyu97.messageconvert.server;

import io.gitee.caoxiaoyu97.messageconvert.common.CxyCloudTempConfigurationUtil;
import io.gitee.caoxiaoyu97.messageconvert.exception.CxyException;
import io.gitee.caoxiaoyu97.util.findclass.FindClassUtil;
import io.gitee.caoxiaoyu97.messageconvert.server.ext.CxyCloudGateWayConvertProcessor;
import io.gitee.caoxiaoyu97.messageconvert.server.ext.CxyCloudGateWayConvertProcessorAll;
import io.gitee.caoxiaoyu97.messageconvert.server.ext.CxyCloudGateWayConvertProcessorFactory;
import io.gitee.caoxiaoyu97.messageconvert.server.service.CxyCloudGateWayService;
import io.gitee.caoxiaoyu97.messageconvert.server.service.CxyCloudGateWayServiceFactory;
import io.gitee.caoxiaoyu97.messageconvert.server.service.CxyCloudGateWayServiceMetadata;
import io.gitee.caoxiaoyu97.util.StrFormatUtils;
import io.gitee.caoxiaoyu97.util.StreamUtil;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;

/*********************************************************
 * 文件名称：CxyCloudServerServiceUtil.java
 * 系统名称：CXY自定义系统
 * 模块名称：io.gitee.caoxiaoyu97.messageconvert.server
 * 功能说明：对外提供服务的工具类
 * 开发人员 @author：caoxy31978
 * 开发时间 @date：2023/7/7 11:02
 * 修改记录：程序版本  修改日期  修改人员  修改单号  修改说明
 *********************************************************/
@Slf4j
public class CxyCloudGateWayServerUtil {
    private static Map<String, Map<String, CxyCloudGateWayServiceMetadata>> groupServicesMap = new LinkedHashMap<>();
    private static Map<String, CxyCloudGateWayServiceMetadata> cacheServicesMap = new HashMap<>();
    private static CxyCloudGateWayServiceFactory cxyCloudGateWayServiceFactory;

    private static CxyCloudGateWayConvertProcessorFactory cxyCloudGateWayConvertProcessorFactory;

    /**
     * 初始化多个模板路径，实际调用从前往后进行模板查找
     *
     * @param groups
     */
    public static void init(String... groups) {
        initCxyCloudGateWayServiceFactory();
        initCxyCloudGateWayConvertProcessorFactory();
        cacheServerXml(groups);
        cacheService();
    }

    private static void initCxyCloudGateWayConvertProcessorFactory() {
        ServiceLoader<CxyCloudGateWayConvertProcessorFactory> processorFactories =
                ServiceLoader.load(CxyCloudGateWayConvertProcessorFactory.class);
        Iterator<CxyCloudGateWayConvertProcessorFactory> iterator = processorFactories.iterator();
        if (iterator.hasNext()) {
            cxyCloudGateWayConvertProcessorFactory = iterator.next();
        } else {
            throw new CxyException("9900",
                    "未找到CxyCloudGateWayConvertProcessorFactory实现，请配置src/main/resources/META-INF/services/" +
                            CxyCloudGateWayConvertProcessorFactory.class.getName());
        }
    }

    /**
     * 缓存服务
     */
    private static void cacheService() {
        groupServicesMap.forEach((k,v)->{
            v.forEach((k1,v1)->{
                cacheServicesMap.putIfAbsent(k1,v1);
            });
        });
    }

    /**
     * 增加处理器
     * @param cxyCloudGateWayServerMetadata
     */
    private static void addProcessor(CxyCloudGateWayServerMetadata cxyCloudGateWayServerMetadata) {
        String extPackage = cxyCloudGateWayServerMetadata.getExtPackage();
        if (extPackage == null || "".equals(extPackage.trim())){
            return;
        }
        Map<String, CxyCloudGateWayServiceMetadata> serviceMetadataMap =
                cxyCloudGateWayServerMetadata.getServiceMetadataMap();
        Set<Class<? extends CxyCloudGateWayConvertProcessor>> classByParentType =
                FindClassUtil.getClassByParentType(CxyCloudGateWayConvertProcessor.class, extPackage);
        Map<String,List<CxyCloudGateWayConvertProcessor>> gateWayConvertProcessorMap =
                classByParentType.stream()
                        .filter(cla -> !cla.isInterface())
                        .map(processorClass -> cxyCloudGateWayConvertProcessorFactory.getCxyCloudGateWayConvertProcessor(processorClass))
                        .filter(Objects::nonNull)
                        .collect(Collectors.groupingBy(CxyCloudGateWayConvertProcessor::getOutCode));
        serviceMetadataMap.forEach((k,v)->{
            v.getProcessorList().addAll(gateWayConvertProcessorMap.getOrDefault(CxyCloudGateWayConvertProcessorAll.allCode,new ArrayList<>()));
            v.getProcessorList().addAll(gateWayConvertProcessorMap.getOrDefault(k,new ArrayList<>()));
        });
    }

    private static void initCxyCloudGateWayServiceFactory() {
        ServiceLoader<CxyCloudGateWayServiceFactory> serviceFactories =
                ServiceLoader.load(CxyCloudGateWayServiceFactory.class);
        Iterator<CxyCloudGateWayServiceFactory> iterator = serviceFactories.iterator();
        if (iterator.hasNext()) {
            cxyCloudGateWayServiceFactory = iterator.next();
        } else {
            throw new CxyException("9900",
                    "未找到CxyCloudGateWayServiceFactory实现，请配置src/main/resources/META-INF/services/" +
                            CxyCloudGateWayServiceFactory.class.getName());
        }
    }

    private static void cacheServerXml(String... groups) {
        for (String group : groups) {
            /**
             * 格式化处理开始
             */
            if (group.startsWith("/")) {
                group = group.substring(1);
            }
            if (group.endsWith("/")) {
                group = group.substring(0,group.length()-1);
            }
            /**
             * 格式化处理结束，处理完的路径为xxxxx/xxxxx
             */
            Enumeration<URL> resources;
            try {
                resources = CxyCloudGateWayServerUtil.class.getClassLoader().getResources(group);
            } catch (IOException e) {
                String msg = group + " 加载失败";
                log.error(msg, e);
                throw new CxyException("9900",msg,e);
            }
            resources.asIterator().forEachRemaining(groupUrl -> {
                CxyCloudGateWayServerMetadata cxyCloudGateWayServerMetadata = loadServicesByGroupUrl(groupUrl);
                addTempData(groupUrl, cxyCloudGateWayServerMetadata);
                CxyCloudTempConfigurationUtil.addConfiguration(groupUrl.toString(), cxyCloudGateWayServerMetadata.getServiceMetadataMap().values().stream().toList());
                addProcessor(cxyCloudGateWayServerMetadata);
                groupServicesMap.put(groupUrl.toString(), cxyCloudGateWayServerMetadata.getServiceMetadataMap());
            });
        }
    }

    /**
     * 加载server.xml
     * @param groupUrl
     * @return
     */
    private static CxyCloudGateWayServerMetadata loadServicesByGroupUrl(URL groupUrl){
        InputStream inputStream;
        try {
            inputStream = new URL(groupUrl + "/" + "server.xml").openStream();
        } catch (IOException e) {
            String msg = groupUrl.toString() + " server.xml 流加载失败";
            log.error(msg, e);
            throw new CxyException("9900",msg,e);
        }
        try {
            return loadServices(inputStream);
        } catch (Exception e) {
            String msg = groupUrl.toString() + " server.xml 处理失败";
            log.error(msg, e);
            throw new CxyException("9900",msg,e);
        }
    }

    /**
     * 添加模板信息
     * @param groupUrl
     * @param cxyCloudGateWayServerMetadata
     */
    private static void addTempData(URL groupUrl,CxyCloudGateWayServerMetadata cxyCloudGateWayServerMetadata) {
        cxyCloudGateWayServerMetadata.getServiceMetadataMap().forEach((k, v) -> {
            v.setTempGroup(groupUrl.toString());
            String outCode = v.getOutCode();
            v.setReqTempName(StrFormatUtils.formatMessage("req/req.{}.ftl", outCode));
            v.setRespTempName(StrFormatUtils.formatMessage("resp/resp.{}.ftl", outCode));
            v.setMockTempName(StrFormatUtils.formatMessage("mock/mock.{}.ftl", outCode));
            v.setReqTemp(getStrByUrl(groupUrl, v.getReqTempName()));
            v.setRespTemp(getStrByUrl(groupUrl, v.getRespTempName()));
            try {
                v.setMockTemp(getStrByUrl(groupUrl, v.getMockTempName()));
            }catch (Exception e){
                if (v.isMock()){
                    throw e;
                }else{
                    v.setMockTemp("");
                }
            }
        });
    }

    private static String getStrByUrl(URL groupUrl, String path) {
        String result;
        try {
            InputStream inputStream = new URL(groupUrl + "/" + path).openStream();
            result = StreamUtil.readBytesAll(inputStream, StandardCharsets.UTF_8);
        } catch (IOException e) {
            String msg = groupUrl.toString() + "/" + path + " 流加载失败";
            throw new CxyException("9900",msg,e);
        }
        return result;
    }

    private static CxyCloudGateWayServerMetadata loadServices(InputStream inputStream) throws Exception {
        Map<String, CxyCloudGateWayServiceMetadata> serverMetadataHashMap = new HashMap<>();
        String defInServiceBeanName = "";
        String extPackage = null;
        Document doc = loadXmlFromInputStream(inputStream);
        Node defInServiceBeanNameNode = doc.getElementsByTagName("inServiceBeanName").item(0);
        if (defInServiceBeanNameNode != null) {
            defInServiceBeanName = ((Element) defInServiceBeanNameNode).getAttribute("name");
        }
        Node extPackageNode = doc.getElementsByTagName("extPackage").item(0);
        if (extPackageNode != null) {
            extPackage = ((Element) extPackageNode).getAttribute("name");
        }
        NodeList nodeList = doc.getElementsByTagName("service");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element element = (Element) nodeList.item(i);
            String outCode = element.getAttribute("outCode");
            String inCode = element.getAttribute("inCode");
            String mock = "".equals(element.getAttribute("mock")) ? "false" : element.getAttribute("mock");
            String inServiceBeanName = "".equals(element.getAttribute("inServiceBeanName")) ? defInServiceBeanName :
                    element.getAttribute("inServiceBeanName");
            CxyCloudGateWayServiceMetadata serverMetadata =
                    new CxyCloudGateWayServiceMetadata(outCode, inCode, inServiceBeanName, Boolean.parseBoolean(mock));
            serverMetadataHashMap.put(outCode, serverMetadata);
        }
        return new CxyCloudGateWayServerMetadata(defInServiceBeanName,extPackage,serverMetadataHashMap);
    }

    private static Document loadXmlFromInputStream(InputStream inputStream) throws Exception {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        dbFactory.setIgnoringElementContentWhitespace(true);
        dbFactory.setValidating(true);
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        dBuilder.setEntityResolver(((publicId, systemId) -> {
            if (publicId.equals("cxycloud-messageconvert-server.dtd")){
                return new InputSource(CxyCloudGateWayServerUtil.class.getClassLoader().getResourceAsStream("io/gitee/caoxiaoyu97/dtd/server.dtd"));
            }
            if (publicId.equals("cxycloud-messageconvert-client.dtd")){
                return new InputSource(CxyCloudGateWayServerUtil.class.getClassLoader().getResourceAsStream("io/gitee/caoxiaoyu97/dtd/client.dtd"));
            }
            return null;
        }));
        Document doc = dBuilder.parse(inputStream);
        doc.getDocumentElement().normalize();
        return doc;
    }

    public static CxyCloudGateWayService getGateWayService(String inServiceBeanName) {
        return cxyCloudGateWayServiceFactory.getCxyCloudGateWayService(inServiceBeanName);
    }

    public static CxyCloudGateWayServiceMetadata getGateWayServerMetadata(String outCode) {
        CxyCloudGateWayServiceMetadata gateWayServerMetadata = cacheServicesMap.get(outCode);
        if (gateWayServerMetadata == null) {
            throw new CxyException("9900", "未找到此服务");
        }
        return gateWayServerMetadata;
    }
    public static Map<String, CxyCloudGateWayServiceMetadata> getAllServices(){
        return Collections.unmodifiableMap(cacheServicesMap);
    }
}
